package com.starlinkdt.common.storage.impl;

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.model.OSSObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.starlinkdt.common.storage.StorageInterface;
import com.starlinkdt.common.storage.config.AliYunOssConfig;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 阿里云对象存储
 *
 * @author system
 */
@Component
public class AliYunOssImpl implements StorageInterface {
    @Autowired
    private AliYunOssConfig aliYunOssConfig;

    /**
     * 创建OSS实例
     *
     * @return AliYunOss
     */
    private OSS generateClient() {
        ClientBuilderConfiguration clientConfig = new ClientBuilderConfiguration();
        clientConfig.setProtocol(Protocol.HTTPS);
        return new OSSClientBuilder().build(
                aliYunOssConfig.getEndpointOss(), aliYunOssConfig.getAccessKeyId(), aliYunOssConfig.getAccessKeySecret(), clientConfig
        );
    }

    /**
     * 关闭OSSClient
     *
     * @param ossClient OSS实例
     */
    private void close(OSS ossClient) {
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }

    /**
     * 获取STS临时令牌
     *
     * @return Map
     */
    @Override
    public Map<String, Object> genStsToken() throws ClientException {
        Map<String, Object> ossMap = new HashMap<>(1);

        // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
        DefaultProfile.addEndpoint(aliYunOssConfig.getRegionId(), "Sts", aliYunOssConfig.getEndpointSts());

        // 构造default profile
        IClientProfile profile = DefaultProfile.getProfile(
                aliYunOssConfig.getRegionId(), aliYunOssConfig.getAccessKeyId(), aliYunOssConfig.getAccessKeySecret()
        );

        // 构造client。
        DefaultAcsClient client = new DefaultAcsClient(profile);
        final AssumeRoleRequest request = new AssumeRoleRequest();
        // 适用于Java SDK 3.12.0及以上版本。
        request.setSysMethod(MethodType.POST);
        request.setRoleArn(aliYunOssConfig.getRoleArn());
        request.setRoleSessionName(aliYunOssConfig.getRoleSessionName());
        request.setDurationSeconds(aliYunOssConfig.getDurationSeconds());
        final AssumeRoleResponse response = client.getAcsResponse(request);

        // 整理数据
        ossMap.put("securityToken", response.getCredentials().getSecurityToken());
        ossMap.put("accessKeyId", response.getCredentials().getAccessKeyId());
        ossMap.put("accessKeySecret", response.getCredentials().getAccessKeySecret());
        ossMap.put("expiration", response.getCredentials().getExpiration());
        ossMap.put("endpoint", aliYunOssConfig.getEndpointOss());
        ossMap.put("bucketName", aliYunOssConfig.getBucketName());

        return ossMap;
    }

    /**
     * 上传Byte数组
     *
     * @param objectName 对象路径及名称
     * @param objectContent byte数组
     */
    @Override
    public void putObjectByte(String objectName, byte[] objectContent) throws IOException {
        // 创建OSSClient实例。
        OSS ossClient = generateClient();

        try (InputStream inputStream = new ByteArrayInputStream(objectContent)) {
            // 上传Byte数组
            ossClient.putObject(aliYunOssConfig.getBucketName(), objectName, inputStream);
        } finally {
            close(ossClient);
        }
    }

    /**
     * 上传网络流
     *
     * @param objectName 对象路径及名称
     * @param fileUrl 待上传的网络文件地址
     */
    @Override
    public void putObjectUrl(String objectName, String fileUrl) throws IOException {
        // 创建OSSClient实例。
        OSS ossClient = generateClient();

        try (InputStream inputStream = new URL(fileUrl).openStream()) {
            ossClient.putObject(aliYunOssConfig.getBucketName(), objectName, inputStream);
        } finally {
            close(ossClient);
        }
    }

    /**
     * 上传文件流
     *
     * @param objectName 对象路径及名称
     * @param filePath 待上传的本地文件路径,需要指定到具体的文件名
     */
    @Override
    public void putObjectFile(String objectName, String filePath) throws IOException {
        // 创建OSSClient实例。
        OSS ossClient = generateClient();

        try (InputStream inputStream = Files.newInputStream(Paths.get(filePath))) {
            ossClient.putObject(aliYunOssConfig.getBucketName(), objectName, inputStream);
        } finally {
            close(ossClient);
        }
    }

    /**
     * 判断文件是否存在
     *
     * @param objectName 对象路径及名称
     * @return boolean
     */
    @Override
    public boolean existObject(String objectName) {
        // 创建OSSClient实例。
        OSS ossClient = generateClient();

        // 判断文件是否存在。如果返回值为true，则文件存在，否则存储空间或者文件不存在
        boolean found = ossClient.doesObjectExist(aliYunOssConfig.getBucketName(), objectName);
        close(ossClient);

        return found;
    }

    /**
     * 获取签名URL
     *
     * @param objectName 对象路径及名称
     * @return String
     */
    @Override
    public String getObjectSignUrl(String objectName) {
        // 创建OSSClient实例。
        OSS ossClient = generateClient();

        // 设置签名URL过期时间为3600秒（1小时）
        Integer oneThousand = 1000;
        Date expiration = new Date(System.currentTimeMillis() + aliYunOssConfig.getDurationSeconds() * oneThousand);
        // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
        URL url = ossClient.generatePresignedUrl(aliYunOssConfig.getBucketName(), objectName, expiration);

        // 关闭OSSClient
        close(ossClient);

        return url.toString();
    }

    /**
     * 下载文件流
     *
     * @param objectName 对象路径及名称
     * @return byte
     */
    @Override
    public byte[] downloadObjectStream(String objectName) throws IOException {
        // 创建OSSClient实例
        OSS ossClient = generateClient();

        try {
            // ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
            OSSObject ossObject = ossClient.getObject(aliYunOssConfig.getBucketName(), objectName);
            InputStream inputStream = ossObject.getObjectContent();
            // 返回文件流
            return IOUtils.toByteArray(inputStream);
        } finally {
            close(ossClient);
        }
    }

    /**
     * 删除文件
     *
     * @param objectName 对象路径及名称
     */
    @Override
    public void deleteObject(String objectName) {
        // 创建OSSClient实例。
        OSS ossClient = generateClient();

        // 删除文件或目录。如果要删除目录，目录必须为空。
        ossClient.deleteObject(aliYunOssConfig.getBucketName(), objectName);

        // 关闭OSSClient
        close(ossClient);
    }

}
